package com.googlecode.jwsm;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.logging.*;

/**
 * @author Matthew D. Hicks
 *
 */
public class ServiceManager {
	private static ServiceManager manager;
	
	private HashMap services;
	private HashMap outgoingXSLTs;
	private HashMap incomingXSLTs;
	
	private ServiceManager() throws IOException {
		services = new HashMap();
		outgoingXSLTs = new HashMap();
		incomingXSLTs = new HashMap();
	}
	
	public void registerService(Service service) {
		services.put(service.getName(), service);
	}
	
	public void unregisterService(Service service) {
		services.remove(service.getName());
	}
	
	public Service getService(String name) {
		return (Service)services.get(name);
	}
	
	public Service[] getServices() {
		Service[] s = new Service[services.size()];
		Iterator iterator = services.values().iterator();
		int i = 0;
		while (iterator.hasNext()) {
			s[i] = (Service)iterator.next();
			i++;
		}
		return s;
	}
	
	public void registerOutgoingXSLT(XSLT xslt) {
		outgoingXSLTs.put(xslt.getName(), xslt);
	}
	
	public void unregisterOutgoingXSLT(XSLT xslt) {
		outgoingXSLTs.remove(xslt.getName());
	}
	
	public XSLT getOutgoingXSLT(String name) {
		return (XSLT)outgoingXSLTs.get(name);
	}
	
	public XSLT getOutgoingXSLT(Class association) {
		XSLT[] xslts = getOutgoingXSLTs();
		for (int i = 0; i < xslts.length; i++) {
			if (association.getName().equals(xslts[i].getDefaultAssociation())) {
				return xslts[i];
			}
		}
		return null;
	}
	
	public XSLT[] getOutgoingXSLTs() {
		XSLT[] x = new XSLT[outgoingXSLTs.size()];
		Iterator iterator = outgoingXSLTs.values().iterator();
		int i = 0;
		while (iterator.hasNext()) {
			x[i] = (XSLT)iterator.next();
			i++;
		}
		return x;
	}
	
	public String[] getServiceParameterTypes() {
		HashSet set = new HashSet();
		Service[] services = getServices();
		XSLT[] xslts = getIncomingXSLTs();
		serviceLoop: for (int i = 0; i < services.length; i++) {
			try {
				Method[] methods = services[i].getMethods();
				methodLoop: for (int j = 0; j < methods.length; j++) {
					if ((methods[j].getParameterTypes().length == 1) && (methods[j].getParameterTypes()[0] != void.class)) {
						xsltLoop: for (int k = 0; k < xslts.length; k++) {
							if (methods[j].getParameterTypes()[0].getName().equals(xslts[k].getDefaultAssociation())) {
								continue methodLoop;
							}
						}
						set.add(methods[j].getParameterTypes()[0].getName());
					}
				}
			} catch(Throwable t) {
				Log.get().log(Level.SEVERE, "Exception occurred while getting service parameter types", t);
			}
		}
		return (String[])set.toArray(new String[set.size()]);
	}
	
	public void registerIncomingXSLT(XSLT xslt) {
		incomingXSLTs.put(xslt.getName(), xslt);
	}
	
	public void unregisterIncomingXSLT(XSLT xslt) {
		incomingXSLTs.remove(xslt.getName());
	}
	
	public XSLT getIncomingXSLT(String name) {
		return (XSLT)incomingXSLTs.get(name);
	}
	
	public XSLT getIncomingXSLT(Class association) {
		XSLT[] xslts = getIncomingXSLTs();
		for (int i = 0; i < xslts.length; i++) {
			if (association.getName().equals(xslts[i].getDefaultAssociation())) {
				return xslts[i];
			}
		}
		return null;
	}
	
	public XSLT[] getIncomingXSLTs() {
		XSLT[] x = new XSLT[incomingXSLTs.size()];
		Iterator iterator = incomingXSLTs.values().iterator();
		int i = 0;
		while (iterator.hasNext()) {
			x[i] = (XSLT)iterator.next();
			i++;
		}
		return x;
	}
	
	public String[] getServiceReturnTypes() {
		HashSet set = new HashSet();
		Service[] services = getServices();
		XSLT[] xslts = getOutgoingXSLTs();
		serviceLoop: for (int i = 0; i < services.length; i++) {
			try {
				Method[] methods = services[i].getMethods();
				methodLoop: for (int j = 0; j < methods.length; j++) {
					if (methods[j].getReturnType() != void.class) {
						xsltLoop: for (int k = 0; k < xslts.length; k++) {
							if (methods[j].getReturnType().getName().equals(xslts[k].getDefaultAssociation())) {
								continue methodLoop;
							}
						}
						set.add(methods[j].getReturnType().getName());
					}
				}
			} catch(Throwable t) {
				Log.get().log(Level.SEVERE, "Exception occurred while getting service return types", t);
			}
		}
		return (String[])set.toArray(new String[set.size()]);
	}
	
	public void stopAll() {
		Service[] services = getServices();
		for (int i = 0; i < services.length; i++) {
			services[i].stop();
		}
		this.services = new HashMap();
		
		XSLT[] xslts = getOutgoingXSLTs();
		for (int i = 0; i < xslts.length; i++) {
			unregisterOutgoingXSLT(xslts[i]);
		}
		xslts = getIncomingXSLTs();
		for (int i = 0; i < xslts.length; i++) {
			unregisterIncomingXSLT(xslts[i]);
		}
	}
	
	public static final ServiceManager getInstance() {
		if (manager == null) {
			try {
				manager = new ServiceManager();
			} catch(Exception exc) {
				exc.printStackTrace();
				throw new RuntimeException(exc);
			}
		}
		return manager;
	}
}
